/**
 * @file creat_insert.c : 用于生成符合要求的文件，第一个字(4B)为文件记录数N,随后N个字表示每个记录的大小(B)
 * @author yfuxzpskbr : 王俊洪
 * @brief 
 * @version 0.1
 * @date 2021-11-16
 * 
 * @copyright Copyright (c) 2021
 * 
 * 为了简单，前一半个数据为Student类型
 * 后一半数据为Rectangle类型
 * 
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <string.h>
#include <errno.h>

#define err_exit(MSG) (perror(MSG), exit(1))

/**
 * @brief 定义结构体，表示本题目中提到的记录
 * 
 */
typedef struct Student /*typedef 取别名*/
{
    int id;
    char name[12];
    short age;
} Student;

typedef struct Rectangle
{
    int width;
    int height;
} Rectangle;

/*函数声明*/
void get_randName(char *dist);
Student generateStudent(int id);
Rectangle generateRectangle();
void create_file(int record_cnt);
void printAll();
void print_At(int index);
int str2int(char *str);

/*用于随机生成姓名*/
char *chrs = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

int main()
{
    //记录个数
    int recorder = 128;
    create_file(recorder);
    printf("记录导入成功...\n");
    while (1)
    {
        printf("请输入你想要查询的记录编号[%d,%d],直接回车退出: ", 1, recorder);
        char buffer[8];
        fgets(buffer, 8, stdin);
        int index = str2int(buffer);
        print_At(index);
    }
    return 0;
}

/**
 * @brief 随机生成一个姓名
 * 
 * @param dist ： 生成姓名的返回参数
 */
void get_randName(char *dist)
{
    //随机生成名字长度，大小2-11
    int len = rand() % 10 + 2;
    for (int i = 0; i < len; i++)
    {
        //播种
        srand((unsigned int)i);
        dist[i] = chrs[rand() % 52];
    }
    dist[len] = '\0'; //添加结尾符
}

/**
 * @brief 生成学生信息
 * 
 * @return Student 
 */
Student generateStudent(int id)
{
    Student s;
    get_randName(s.name);
    s.id = id;
    id++;
    srand((unsigned int)(time(NULL) + id));
    s.age = rand() % 10 + 15;
    return s;
}

/**
 * @brief 生成一个矩形
 * 
 * @return Rectangle 
 */
Rectangle generateRectangle(int index)
{
    Rectangle r;
    srand((unsigned int)(time(NULL) + index));
    r.height = rand() % 32767;
    srand((unsigned int)(time(NULL) + index * 2));
    r.width = rand() % 32767;
    return r;
}

/**
 * @brief 创建一份符合题目的文件：第一个字(4B)为文件记录数N,随后N个字表示每个记录的大小(B)，随后才是N个记录
 * 
 * @param record_cnt 需要创建的记录的数量
 */
void create_file(int record_cnt)
{
    Student s;
    Rectangle r;                           //临时记录,将会被按格式写入文件
    FILE *file = fopen("source.wjh", "w"); //保存生成的数据文件，方便后面比对
    if (file == NULL)
    {
        err_exit("File create error!");
    }
    //创建文件
    int fd = open("data.wjh", O_WRONLY | O_CREAT, 0644);
    if (fd == -1)
    {
        err_exit("File create error!");
    }
    //先按照格式写入文件
    write(fd, (char *)&record_cnt, sizeof(record_cnt)); //文件第一个字记录文件的总记录数
    //记录已经写入的总字节
    int bytes = 0; //方便计算每个记录的偏移量
    //循环写入数据
    for (int i = 0; i < record_cnt; i++)
    {
        if (i < record_cnt / 2)
        {
            //生成学生信息
            s = generateStudent(i + 1);
            //记录的字节数记录在随后的n个字：1：1 ,2:2,3:3
            int t = sizeof(s); //求出s所占的字节数
            //插入第i个记录的长度，需要移动文件指针
            lseek(fd, 4 * (i + 1), SEEK_SET);
            write(fd, &t, sizeof(int)); //一个字
            //写入数据
            lseek(fd, (1 + record_cnt) * 4 + bytes, SEEK_SET);
            //写入一条记录
            write(fd, &s, t);
            //统计目前已经写入的字节数，用于计算偏移量
            bytes += t;
            //输出到file文件用于后期对比，相当于拷贝一份副本
            fprintf(file, "id : %d , name : %s , age : %d\n", s.id, s.name, s.age);
        }
        else
        {
            //生成矩阵信息
            r = generateRectangle(i);
            //记录的字节数记录在随后的n个字：1：1 ,2:2,3:3
            int t = sizeof(r); //求出s所占的字节数
            //插入第i个记录的长度，需要移动文件指针
            lseek(fd, 4 * (i + 1), SEEK_SET);
            write(fd, &t, sizeof(int)); //一个字
            //写入数据
            lseek(fd, (1 + record_cnt) * 4 + bytes, SEEK_SET);
            //写入一条记录
            write(fd, &r, t);
            //统计目前已经写入的字节数，用于计算偏移量
            bytes += t;
            //输出到file文件用于后期对比，相当于拷贝一份副本
            fprintf(file, "width = %d , height = %d\n", r.width, r.height);
        }
    }
    //关闭文件描述字
    close(fd);
    fclose(file);
}

/**
 * @brief 打印所有记录，如果发现和source.wjh不相同，就证明程序发生了错误
 * 
 */
void printAll()
{
    Student s;
    Rectangle r;
    int fd = open("data.wjh", O_RDONLY);
    if (fd == -1)
    {
        err_exit("File create error!");
    }
    //读取记录个数
    int cnt;
    read(fd, &cnt, sizeof(int));
    // 读取每一条记录
    int t = 0, sum = 0; //t表示当前记录的字节数，sum表示已经读了的字节数的总和
    for (int i = 0; i < cnt; i++)
    {
        //读取记录的大小(B)
        lseek(fd, 4 * (i + 1), SEEK_SET);
        read(fd, &t, sizeof(int));

        //找到记录的起始位置开始读
        lseek(fd, (cnt + 1) * 4 + sum, SEEK_SET);
        if (i < cnt / 2)
        {
            //读取一条记录
            read(fd, &s, t);
            // 输出学生信息看是否有误
            printf("id : %d , name : %s , age : %d\n", s.id, s.name, s.age);
        }
        else
        {
            read(fd, &r, t);
            printf("width = %d , height = %d\n", r.width, r.height);
        }
        sum += t; //统计前面的总字节数
    }
    close(fd);
}

/**
 * @brief 打印指定记录编号的记录
 * 
 * @param index 下标从1开始 
 */
void print_At(int index)
{
    Rectangle r;
    Student s;
    int fd = open("data.wjh", O_RDONLY);
    if (fd == -1)
    {
        err_exit("File create error!");
    }
    //读取记录个数
    int cnt;
    read(fd, &cnt, sizeof(int)); //读取记录个数
    if (index > cnt || index <= 0)
    {
        // err_exit("index range error!"); //出错就退出，不希望退出，只希望本次过程不执行，因此使用return
        printf("Index range error!\n");
        return;
    }
    // 遍历每一条记录，找到要读取记录的偏移量
    int t = 0, sum = 0; //t表示当前记录的字节数，sum表示已经读了的字节数的总和
    for (int i = 1; i < index; i++)
    {
        //读取记录的大小(B)
        lseek(fd, 4 * i, SEEK_SET);
        read(fd, &t, sizeof(int));
        sum += t; //统计预读取记录前面的总字节数
    }
    //先找到预读取记录的长度
    lseek(fd, 4 * index, SEEK_SET);
    read(fd, &t, sizeof(int));
    //找到记录的起始位置开始读
    lseek(fd, (1 + cnt) * 4 + sum, SEEK_SET);
    if (index < cnt / 2)
    {
        //读取一条记录
        read(fd, &s, t);
        // 输出学生信息看是否有误
        printf("id : %d , name : %s , age : %d\n", s.id, s.name, s.age);
    }
    else
    {
        read(fd, &r, t);
        printf("width = %d , height = %d\n", r.width, r.height);
    }
    close(fd);
}

/**
 * @brief 将字符串转化为数字
 * 
 * @return int 
 */
int str2int(char *str)
{
    //直接敲回车退出
    if (strlen(str) == 1)
        exit(0);
    /*使用fgets默认读入换行符，因此要去掉*/
    str[strlen(str) - 1] = '\0';
    char buffer[8];
    sprintf(buffer, "%s", str);
    int index;
    sscanf(buffer, "%d", &index);
    return index;
}